using System;
using System.Xml;

namespace ServiceModel.Syndication.LEAP2A {
    /// <summary>
    /// The type of the point in time represented by a LeapDate
    /// </summary>
    public enum LeapDatePoints {
        /// <summary>
        /// LeapDate represents the start of an activity
        /// </summary>
        Start,

        /// <summary>
        /// LeapDate represents the end of an activity
        /// </summary>
        End,

        /// <summary>
        /// LeapDate represents the target date of an activity
        /// </summary>
        Target,
    }

    /// <summary>
    /// Used to represent time information connected with the subject matter of a record
    /// </summary>
    public class LeapDate {
        /// <summary>
        /// Initializes a new instance of the <see cref="LeapDate"/> class.
        /// </summary>
        public LeapDate() {
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance has time component,
        /// if set to true this time will be serialized into the Atom feed, otherwise
        /// only the date will be included.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has time component; otherwise, <c>false</c>.
        /// </value>
        public bool HasTimeComponent {
            get;
            set;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapDate"/> class, 
        /// with a type and value of the date.
        /// </summary>
        /// <param name="point">The type of date.</param>
        /// <param name="date">The date of the event.</param>
        public LeapDate(LeapDatePoints point, DateTime date) {
            this.Point = point;
            this.Value = date;
            this.HasTimeComponent = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapDate"/> class,
        /// with a type and value of the date.
        /// </summary>
        /// <param name="point">The type of date.</param>
        /// <param name="date">The date of the event.</param>
        /// <param name="hasTimeComponent">if set to <c>true</c> the date has a time which should be saved.</param>
        public LeapDate(LeapDatePoints point, DateTime date, bool hasTimeComponent) {
            this.Point = point;
            this.Value = date;
            this.HasTimeComponent = hasTimeComponent;
        }

        /// <summary>
        /// Gets or sets the value of the date.
        /// </summary>
        /// <value>The date value.</value>
        public DateTime Value {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the label associated with the date.
        /// </summary>
        /// <value>The human readable label.</value>
        public string Label {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the type of date.
        /// </summary>
        /// <value>The date type from LeapDatePoints.</value>
        public LeapDatePoints Point {
            get;
            set;
        }

        /// <summary>
        /// Writes the date to and Atom feed.
        /// </summary>
        /// <param name="writer">The writer for the feed.</param>
        public void WriteTo(XmlWriter writer, LeapFeed feed) {
            writer.WriteStartElement(
                        feed.LeapPredicatesNamespacePrefix,
                        "date", feed.LeapPredicatesNamespace);

            writer.WriteAttributeString(
                feed.LeapPredicatesNamespacePrefix,
                "point",
                feed.LeapPredicatesNamespace,
                this.Point.ToString().ToLower());

            if (!string.IsNullOrEmpty(this.Label)) {
                writer.WriteAttributeString(
                    feed.LeapPredicatesNamespacePrefix,
                    "label",
                    feed.LeapPredicatesNamespace,
                    this.Label);
            }

            if (this.HasTimeComponent) {
                writer.WriteString(this.Value.ToString("s"));
            } else {
                writer.WriteString(this.Value.ToString("yyyy-MM-dd"));
            }

            writer.WriteEndElement();
        }

        /// <summary>
        /// Try to parse a LeapDate from an Atom feed.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        public bool TryParseLeapDate(XmlReader reader) {
            bool okay = false;

            if (reader.HasAttributes) {
                // Get the date point from the documented location
                string pt = reader.GetAttribute(reader.Prefix + ":point");

                /* Try finding the point in the ennumerated point values, skip to the next
                 * item if it fails */
                try {
                    this.Point = (LeapDatePoints)Enum.Parse(typeof(LeapDatePoints), pt, true);
                } catch {
                    return false;
                }

                this.Label = reader.GetAttribute(reader.Prefix + ":label");
            }

            while (reader.NodeType != XmlNodeType.EndElement) {
                reader.Read();

                if (reader.NodeType == XmlNodeType.Text) {
                    DateTime date;
                    if (DateTime.TryParse(reader.Value, out date)) {
                        this.Value = date;
                        okay = true;
                    }
                }
            }

            return okay;
        }
    }
}
